package com.ziytek.web.citizen.service;

import com.google.gson.Gson;
import com.ziytek.web.citizen.config.caffeine.CaffeineUtil;
import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.mapper.BoardConfigMapper;
import com.ziytek.web.citizen.model.bean.Batteries;
import com.ziytek.web.citizen.model.bean.BrattyRealData;
import com.ziytek.web.citizen.model.bean.CapStatsVo;
import com.ziytek.web.citizen.model.bean.OneBoardRealData;
import com.ziytek.web.citizen.model.vo.RealTimeDo;
import com.ziytek.web.citizen.model.vo.RealTimeVo;
import com.ziytek.web.citizen.pojo.basic.BoardConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 单电池通道数据处理
 *
 * @author fenghx
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OneBatteryService {
    private final Gson gson;
    private final BoardConfigMapper boardConfigMapper;
    private final CompressionService compressionService;
    private final BatteryStatsService batteryStatsService;

    private static final String CACHE_KEY_BOARD = "CACHE_KEY_BOARD_KEY_";
    private static final String CACHE_KEY_MAP = "CACHE_KEY_MAP_KEY_";

    public void oneBratty(int cabinetNo, int boardNo, int channelNo, CapStatsVo vo) {
        Assert.notNull(vo, "信息推送 vo is null");

        String key = CACHE_KEY_BOARD + boardNo + CaffeineUtil.CACHE_KEY_SYMBOL + channelNo;
        BoardConfig boardConfig = CaffeineUtil.get(key);
        if (null == boardConfig) {
            boardConfig = boardConfigMapper.queryByChannelNo(boardNo, channelNo);
            if (null == boardConfig) {
                log.info("oneBratty boardConfig is null key:{}", key);
                return;
            }
            // 放到缓存
            CaffeineUtil.set(key, boardConfig, 12 * 60 * 60);
        }

        // 那扇门
        String door = boardConfig.getDoor();
        String mapKey = CACHE_KEY_MAP + cabinetNo + CaffeineUtil.CACHE_KEY_SYMBOL + door;

        RealTimeDo rd = batteryStatsService.getRealTimeVo(vo, cabinetNo + "");
        Map<String, Set<RealTimeDo>> map = CaffeineUtil.get(mapKey);
        if (CollectionUtils.isEmpty(map)) {
            map = new ConcurrentHashMap<>();
        }

        Set<RealTimeDo> list = map.get(door);
        if (CollectionUtils.isEmpty(list)) {
            list = new HashSet<>();
        }

        list.removeIf(v -> v.getCnn() == channelNo && v.getCn() == cabinetNo && v.getBn() == boardNo);
        list.add(rd);
        map.put(door, list);
        CaffeineUtil.set(mapKey, map);
        //log.info("map数据首页长连接推送数据耗时：{}-{}", door, list.size());
    }

    public BrattyRealData oneBrattyRealData(String cabinetNo, String door) {

        Assert.hasLength(door, "信息推送 door is null");

        long start = System.currentTimeMillis();
        String mapKey = CACHE_KEY_MAP + cabinetNo + CaffeineUtil.CACHE_KEY_SYMBOL + door;

        Map<String, Set<RealTimeDo>> realTimeDoMap = CaffeineUtil.get(mapKey);
        if (CollectionUtils.isEmpty(realTimeDoMap)) {
            //log.info("test-map数据首页长连接推送数据为空：{}", mapKey);
            return getEmptyBrattyRealData(door);
        }

        var brattyRealData = getBrattyRealData(realTimeDoMap, door);
        if (CollectionUtils.isEmpty(brattyRealData)) {
            //log.info("test-brattyRealData数据首页长连接推送数据为空：{}", mapKey);
            return getEmptyBrattyRealData(door);
        }

        //log.info("首页长连接推送第二次处理数据耗时：{}", System.currentTimeMillis() - start);
        return brattyRealData.getFirst();
    }


    protected List<BrattyRealData> getBrattyRealData(Map<String, Set<RealTimeDo>> realTimeDoMap, String queryDoor) {
        if (CollectionUtils.isEmpty(realTimeDoMap) || StringUtils.isEmpty(queryDoor)) {
            return Collections.emptyList();
        }

        return getBrattyRealData(getStringBatteriesMap(realTimeDoMap, queryDoor));
    }

    private BrattyRealData getEmptyBrattyRealData(String door) {
        BrattyRealData b = new BrattyRealData();
        b.setDoor(door);
        List<OneBoardRealData> doorData = new ArrayList<>();

        for (int i = 1; i <= NumConstant.INT_FOUR; i++) {
            OneBoardRealData oneBoardRealData = new OneBoardRealData();
            oneBoardRealData.setLine(String.valueOf(i));
            List<Batteries> batteriesList = new ArrayList<>();
            for (int j = 1; j <= NumConstant.INT_EIGHT; j++) {
                Batteries batteries = new Batteries();
                batteries.setPosition(j);
                batteries.setCurrent(0F);
                batteries.setVoltage(0F);
                batteries.setCapacity(0F);
                batteries.setExecutionDuration(0F);
                batteries.setExceptionType(2);
                batteriesList.add(batteries);
            }
            oneBoardRealData.setBatteriesList(batteriesList);
            doorData.add(oneBoardRealData);
        }

        b.setDoorData(doorData);
        return b;
    }

    private Map<String, Batteries> getStringBatteriesMap(Map<String, Set<RealTimeDo>> realTimeDoMap, String doorNos) {
        Map<String, Batteries> batteriesMap = new ConcurrentHashMap<>();
        for (var entry : realTimeDoMap.entrySet()) {
            if (!doorNos.equalsIgnoreCase(entry.getKey())) {
                continue;
            }

            for (var o : entry.getValue()) {
                extracted(doorNos, o, batteriesMap);
            }
        }

        return batteriesMap;
    }

    private void extracted(String doorNos, RealTimeDo o, Map<String, Batteries> batteriesMap) {
        var cellNum = o.getCnn();
        var boardConfig = boardConfigMapper.queryByChannelNo(o.getBn(), cellNum);
        if (null == boardConfig || !doorNos.equalsIgnoreCase(boardConfig.getDoor())) {
            return;
        }

        var door = boardConfig.getDoor();
        var lineNo = boardConfig.getLineNo();
        var key = door + "," + lineNo + "," + cellNum;

        byte[] data;
        try {
            data = compressionService.decompress(o.getData());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        var vo = gson.fromJson(new String(data, StandardCharsets.UTF_8), RealTimeVo.class);
        if (null != vo) {
            batteriesMap.put(key, getBatteries(cellNum, vo));
        }
    }

    private Batteries getBatteries(int cellNum, RealTimeVo v) {
        var batteries = new Batteries();
        batteries.setPosition(cellNum);
        batteries.setCurrent(v.getC() * NumConstant.FLOAT_ONE);
        batteries.setVoltage(v.getV() * NumConstant.FLOAT_ONE);

        int t = ((Double) v.getTs()).intValue() / NumConstant.INT_TEN;
        int y = ((Double) v.getTs()).intValue() % NumConstant.INT_TEN;
        int time = y == NumConstant.INT_ZERO ? t * NumConstant.INT_TEN : (t + NumConstant.INT_ONE) * NumConstant.INT_TEN;

        var duration = String.format("%.2f", (time / NumConstant.FLOAT_SIXTY));

        batteries.setExecutionDuration(Float.parseFloat(duration));
        var capacity = String.format("%.2f", (v.getCpa()));
        batteries.setCapacity(Float.parseFloat(capacity));
        batteries.setExceptionType(v.getT());
        batteries.setCommandType(v.getCty());

        return batteries;
    }

    private List<BrattyRealData> getBrattyRealData(Map<String, Batteries> batteriesMap) {
        if (CollectionUtils.isEmpty(batteriesMap)) {
            return Collections.emptyList();
        }

        List<BrattyRealData> realDataList = new ArrayList<>();
        Map<String, OneBoardRealData> oneBoardRealDataVoHashMap = new ConcurrentHashMap<>();
        for (var key : batteriesMap.keySet()) {
            toOneBoardRealDataMap(batteriesMap, key, oneBoardRealDataVoHashMap);
        }

        Map<String, BrattyRealData> brattyRealDataMap = new HashMap<>();
        for (var key : oneBoardRealDataVoHashMap.keySet()) {
            toBrattyRealDataMap(key, brattyRealDataMap, oneBoardRealDataVoHashMap);
        }

        for (var entry : brattyRealDataMap.entrySet()) {
            realDataList.add(entry.getValue());
        }
        return realDataList;
    }

    private void toBrattyRealDataMap(String key, Map<String, BrattyRealData> brattyRealDataMap, Map<String, OneBoardRealData> oneBoardRealDataVoHashMap) {
        var split = key.split(",");
        var door = split[NumConstant.INT_ZERO];
        var line = split[NumConstant.INT_ONE];

        var brattyRealData = brattyRealDataMap.get(door);
        if (null == brattyRealData) {
            brattyRealData = new BrattyRealData();
            brattyRealData.setDoor(door);
        }

        var doorData = brattyRealData.getDoorData();
        if (null == doorData) {
            doorData = new ArrayList<>();
        }

        var has = doorData.stream().anyMatch(d -> null != d && StringUtils.isNotBlank(d.getLine()) && d.getLine().equalsIgnoreCase(line));
        var oneBoardRealData = oneBoardRealDataVoHashMap.get(key);
        if (!has) {
            doorData.add(oneBoardRealData);
            brattyRealData.setDoorData(doorData);
        }

        brattyRealDataMap.put(door, brattyRealData);
    }

    private void toOneBoardRealDataMap(Map<String, Batteries> batteriesMap, String key, Map<String, OneBoardRealData> oneBoardRealDataVoHashMap) {
        var split = key.split(",");
        var door = split[NumConstant.INT_ZERO];
        var line = split[NumConstant.INT_ONE];
        var cellNum = split[NumConstant.INT_TWO];

        var oneBoardRealData = oneBoardRealDataVoHashMap.get(door + "," + line);
        if (null == oneBoardRealData) {
            oneBoardRealData = new OneBoardRealData();
            oneBoardRealData.setLine(line);
        }

        var batteries = batteriesMap.get(key);
        var batteriesList = oneBoardRealData.getBatteriesList();
        if (null == batteriesList) {
            batteriesList = new ArrayList<>();
        }

        var has = batteriesList.stream().anyMatch(b -> null != b && Integer.parseInt(cellNum) == b.getPosition());
        if (!has) {
            batteriesList.add(batteries);
            batteriesList.sort(Comparator.comparing(Batteries::getPosition));
            oneBoardRealData.setBatteriesList(batteriesList);
        }

        oneBoardRealDataVoHashMap.put(door + "," + line, oneBoardRealData);
    }

}
